package tokens

import (
	"encoding/json"
	"fmt"

	"sync"
	"time"

	"gitee.com/littletow/qywx/services/caches"
	"gitee.com/littletow/qywx/services/commons"
)

const (
	//AccessTokenURL 获取access_token的接口
	accessTokenURL = "https://qyapi.weixin.qq.com/cgi-bin/gettoken"
	//CacheKeyQywxPrefix 企业微信cache key前缀
	CacheKeyQywxPrefix = "qywx_"
)

// AccessTokenStore accessToken仓库，管理accessToken
type AccessTokenStore struct {
	tokens map[string]string // 管理存放token，对应的cache中键值
	caches caches.Caches     // 关联的存储
	mutex  sync.RWMutex      // 添加时防止冲突
}

// AccessToken 企业微信access token
type AccessToken struct {
	AccessToken string
	ExpiresIn   int64
}

// AccessTokenReq 获取access token的请求
type AccessTokenReq struct {
	CorpID  string `json:"corpid"`
	AgentID string `json:"agentid"` // 如果agentid不存在，使用commons中的常量
	Secret  string `json:"corpsecret"`
}

// AccessTokenResp access token的响应
type AccessTokenResp struct {
	commons.CommonError

	AccessToken string `json:"access_token"`
	ExpiresIn   int64  `json:"expires_in"`
}

// NewAccessTokenStore 创建一个新的accesstoken仓库
func NewAccessTokenStore(caches caches.Caches) *AccessTokenStore {
	return &AccessTokenStore{
		tokens: make(map[string]string),
		caches: caches,
	}
}

// setToken 设置token，暂时不放开
func (store *AccessTokenStore) setToken(req AccessTokenReq) (string, error) {
	// 仓库中没有找到，或在缓存中没有找到，都重新从服务器获取
	resp, err := getTokenFromServer(req.CorpID, req.Secret)
	if err != nil {
		return "", err
	}
	expires := resp.ExpiresIn - 120
	accessTokenCacheKey := CacheKeyQywxPrefix + req.AgentID
	err = store.caches.Set(accessTokenCacheKey, resp.AccessToken, time.Duration(expires)*time.Second)
	if err != nil {
		return "", err
	}
	// 加锁
	store.mutex.Lock()
	defer store.mutex.Unlock()
	store.tokens[req.AgentID] = accessTokenCacheKey
	return resp.AccessToken, nil
}

// GetToken 获取token,根据appID 获取token，这里将设置和获取合并到一起，方便上层调用。
func (store *AccessTokenStore) GetToken(agentID string, req AccessTokenReq) (string, error) {
	// 检查仓库中是否存在该agentID的token，没有存在，就需要从服务器获取，并存放到仓库中
	tokenCacheKey, ok := store.tokens[agentID]
	// 如果存在
	if ok {
		// 从缓存中获取token
		val := store.caches.Get(tokenCacheKey)
		if val != nil {
			token := val.(string)
			return token, nil
		}
		// 缓存中不存在，需要从服务器获取
	}
	token, err := store.setToken(req)
	if err != nil {
		return "", err
	}
	return token, nil
}

// getTokenFromServer 强制从企业微信服务器获取token
// 参考https://open.work.weixin.qq.com/api/doc/90000/90135/91039
// 请求地址： https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET
func getTokenFromServer(corpID, corpSecret string) (accessTokenResp AccessTokenResp, err error) {
	url := fmt.Sprintf("%s?corpid=%s&corpsecret=%s", accessTokenURL, corpID, corpSecret)
	var body []byte
	body, err = commons.HTTPGet(url)
	if err != nil {
		return
	}
	err = json.Unmarshal(body, &accessTokenResp)
	if err != nil {
		return
	}
	// 出错返回码，为0表示成功，非0表示调用失败
	if accessTokenResp.ErrCode != 0 {
		err = fmt.Errorf("GetTokenFromServer error : errcode=%v , errormsg=%v", accessTokenResp.ErrCode, accessTokenResp.ErrMsg)
		return
	}
	return
}
